#include "apdu_ecc_manager.h"

#include "../../mk_lib/mk_utility.h"

#include <string.h>
#include <memory.h>
#include <stdlib.h>

#define  MAX_ECC_TIMOUT 2000

apdu_ecc_manager::apdu_ecc_manager( void )
{

}


apdu_ecc_manager::~apdu_ecc_manager( void )
{

}


apdu * apdu_ecc_manager::create_apdu_gen_ecc_keypair( int app_id, int container_id, int bits )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x70, 0x00,0x00, "GenECCKeyPair");
	newApdu->set_comm_timeout(MAX_ECC_TIMOUT);

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_dword_value_be(bits, data + pos);
	pos += 4;

	newApdu->set_lc_data(data, pos);

	return newApdu;

}

apdu * apdu_ecc_manager::create_apdu_import_ecc_keypair( int app_id, int container_id, 
														unsigned char * enveloped_key_blob, int key_blob_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x72, 0x00,0x00, "ImportECCKeyPair");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;


	memcpy(data + pos, enveloped_key_blob, key_blob_len);
	pos += key_blob_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_sign_data( int app_id, int container_id,
												   int input_mode, unsigned char * user_id, int id_len, 
												   unsigned char * input_data, int data_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x74, input_mode, 0, "ECCSignData");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	if(input_mode != 2)
	{
		mk_utility::fill_buff_with_dword_value_be(id_len, data + pos);
		pos += 4;

		memcpy(data + pos, user_id, id_len);
		pos += id_len;
	}

	memcpy(data + pos, input_data, data_len);
	pos += data_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_sign_data_ex(int app_id, int container_id, int input_mode, int key_spec,
									unsigned char * user_id, int id_len, 
									unsigned char * input_data, int data_len)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0x2A, input_mode, key_spec, "ECCSignDataEx");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	if(input_mode != 2)
	{
		mk_utility::fill_buff_with_dword_value_be(id_len, data + pos);
		pos += 4;

		memcpy(data + pos, user_id, id_len);
		pos += id_len;
	}

	memcpy(data + pos, input_data, data_len);
	pos += data_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_sign_data_interactive(int app_id, int container_id, int input_mode, int key_spec,int timeout,
													  unsigned char * user_id, int id_len, 
													  unsigned char * input_data, int data_len)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0xA2, input_mode, key_spec, "ECCSignDataInteractive");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(timeout, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(app_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	if(input_mode != 2)
	{
		mk_utility::fill_buff_with_dword_value_be(id_len, data + pos);
		pos += 4;

		memcpy(data + pos, user_id, id_len);
		pos += id_len;
	}

	memcpy(data + pos, input_data, data_len);
	pos += data_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_sign_data_interactive_cancel(int app_id, int container_id, int input_mode, int key_spec)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_1, 0xC0, 0xA2, input_mode, key_spec, "ECCSignDataInteractiveCancel");
	
	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_verify( unsigned char * ecc_pub_key_blob, int key_blob_len,
												unsigned char * input_data, unsigned long data_len,
												unsigned char * ecc_signature_blob, int sig_blob_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x76, 0, 0, "ECCVerify");

	unsigned char data[4096] = {0};
	int pos = 0;

	memcpy(data, ecc_pub_key_blob, key_blob_len);
	pos += key_blob_len;

	mk_utility::fill_buff_with_dword_value_be(data_len, data + pos);
	pos += 4;

	memcpy(data + pos, input_data, data_len);
	pos += data_len;

	memcpy(data + pos, ecc_signature_blob, sig_blob_len);
	pos += sig_blob_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_export_session_key( int app_id, int container_id, unsigned long alg_id, 
															unsigned char * ecc_pub_key_blob, unsigned long key_blob_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x78, 0, 0, "ECCExportSessionKey");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	memcpy(data + pos, ecc_pub_key_blob, key_blob_len);
	pos += key_blob_len;

	mk_utility::fill_buff_with_dword_value_be(alg_id, data + pos);
	pos += 4;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_export_session_key_ex( int app_id, int container_id, int session_key_id, 
															   unsigned char * ecc_pub_key_blob, unsigned long key_blob_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x80, 0, 0, "ECCExportSessionKeyEx");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(session_key_id, data + pos);
	pos += 2;

	memcpy(data + pos, ecc_pub_key_blob, key_blob_len);
	pos += key_blob_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_export_session_key2( int app_id, int container_id, int session_key_id, 
															   unsigned char * ecc_pub_key_blob, unsigned long key_blob_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x80, 0x01, 0, "ECCExportSessionKeyEx"); //export all 48 bytes 

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(session_key_id, data + pos);
	pos += 2;

	memcpy(data + pos, ecc_pub_key_blob, key_blob_len);
	pos += key_blob_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}


apdu * apdu_ecc_manager::create_apdu_ext_ecc_encrypt( unsigned char * ecc_pub_key_blob, unsigned long key_blob_len, 
													 unsigned char * input_data, unsigned long data_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x7A, 0, 0, "ExECCEncrypt");

	unsigned char data[4096] = {0};

	int pos = 0;
	memcpy(data, ecc_pub_key_blob, key_blob_len);
	pos += key_blob_len;

	mk_utility::fill_buff_with_dword_value_be(data_len, data + pos);
	pos += 4;

	memcpy(data + pos, input_data, data_len);
	pos += data_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_gen_agreement_data_with_ecc( int app_id, int container_id, unsigned long alg_id,
																 unsigned char * user_id, int id_len)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x82, 0, 0, "GenerateAgreementDataWithECC");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_dword_value_be(alg_id, data + pos);
	pos += 4;

	mk_utility::fill_buff_with_dword_value_be(id_len, data + pos);
	pos += 4;

	memcpy(data + pos, user_id, id_len);
	pos += id_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_gen_agreement_data_and_key_with_ecc( int app_id, int container_id, unsigned long alg_id,
																		 unsigned char * sponsor_pub_key_blob,
																		 unsigned long sponsor_key_blob_len, 
																		 unsigned char * tmp_pub_key_blob, 
																		 unsigned long tmp_key_blob_len, 
																		 unsigned char * sponsor_id, 
																		 int sponsor_id_len, 
																		 unsigned char * responser_id, 
																		 int responser_id_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x84, 0, 0, "GenerateAgreementDataAndKeyWithECC");
	newApdu->set_comm_timeout(MAX_ECC_TIMOUT);

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_dword_value_be(alg_id, data + pos);
	pos += 4;

	memcpy(data + pos, sponsor_pub_key_blob, sponsor_key_blob_len);
	pos += sponsor_key_blob_len;

	memcpy(data + pos, tmp_pub_key_blob, tmp_key_blob_len);
	pos += tmp_key_blob_len;

	mk_utility::fill_buff_with_dword_value_be(sponsor_id_len, data + pos);
	pos += 4;

	memcpy(data + pos, sponsor_id, sponsor_id_len);
	pos += sponsor_id_len;

	mk_utility::fill_buff_with_dword_value_be(responser_id_len, data + pos);
	pos += 4;

	memcpy(data + pos, responser_id, responser_id_len);
	pos += responser_id_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}


apdu * apdu_ecc_manager::create_apdu_gen_agreement_data_and_key_with_ecc_ex( int app_id, int container_id, int key_spec,
																		 unsigned long alg_id,
																		 unsigned char * sponsor_pub_key_blob,
																		 unsigned long sponsor_key_blob_len, 
																		 unsigned char * tmp_pub_key_blob, 
																		 unsigned long tmp_key_blob_len, 
																		 unsigned char * sponsor_id, 
																		 int sponsor_id_len, 
																		 unsigned char * responser_id, 
																		 int responser_id_len )
{
	//for compatible with the previous COS
	if(key_spec == 0) 
		key_spec = 1;
	else
		key_spec = 0;

	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x84, key_spec, 0, "GenerateAgreementDataAndKeyWithECC");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_dword_value_be(alg_id, data + pos);
	pos += 4;

	memcpy(data + pos, sponsor_pub_key_blob, sponsor_key_blob_len);
	pos += sponsor_key_blob_len;

	memcpy(data + pos, tmp_pub_key_blob, tmp_key_blob_len);
	pos += tmp_key_blob_len;

	mk_utility::fill_buff_with_dword_value_be(sponsor_id_len, data + pos);
	pos += 4;

	memcpy(data + pos, sponsor_id, sponsor_id_len);
	pos += sponsor_id_len;

	mk_utility::fill_buff_with_dword_value_be(responser_id_len, data + pos);
	pos += 4;

	memcpy(data + pos, responser_id, responser_id_len);
	pos += responser_id_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_gen_key_with_ecc( int app_id, int container_id, unsigned long agreement_handle,
													  unsigned char * responser_pub_key_blob, unsigned long responser_key_blob_len,
													  unsigned char * tmp_pub_key_blob, unsigned long tmp_key_blob_len,
													  unsigned char * responser_id, int responser_id_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x86, 0, 0, "GenerateKeyWithECC");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_dword_value_be(agreement_handle, data + pos);
	pos += 4;

	memcpy(data + pos, responser_pub_key_blob, responser_key_blob_len);
	pos += responser_key_blob_len;

	memcpy(data + pos, tmp_pub_key_blob, tmp_key_blob_len);
	pos += tmp_key_blob_len;

	mk_utility::fill_buff_with_dword_value_be(responser_id_len, data + pos);
	pos += 4;

	memcpy(data + pos, responser_id, responser_id_len);
	pos += responser_id_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}


apdu * apdu_ecc_manager::create_apdu_gen_key_with_ecc_ex( int app_id, int container_id, int key_spec, unsigned long agreement_handle,
													  unsigned char * responser_pub_key_blob, unsigned long responser_key_blob_len,
													  unsigned char * tmp_pub_key_blob, unsigned long tmp_key_blob_len,
													  unsigned char * responser_id, int responser_id_len )
{
	//for compatible with the previous COS
	if(key_spec == 0) 
		key_spec = 1;
	else
		key_spec = 0;

	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x86, key_spec, 0, "GenerateKeyWithECC");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_dword_value_be(agreement_handle, data + pos);
	pos += 4;

	memcpy(data + pos, responser_pub_key_blob, responser_key_blob_len);
	pos += responser_key_blob_len;

	memcpy(data + pos, tmp_pub_key_blob, tmp_key_blob_len);
	pos += tmp_key_blob_len;

	mk_utility::fill_buff_with_dword_value_be(responser_id_len, data + pos);
	pos += 4;

	memcpy(data + pos, responser_id, responser_id_len);
	pos += responser_id_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ecc_private_decrypt( int app_id, int container_id, int key_spec,
														 unsigned char * input_data, unsigned long data_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0xC0, 0x28, key_spec, 0, "ECCPriDecrypt");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_dword_value_be(data_len, data + pos);
	pos += 4;

	memcpy(data + pos, input_data, data_len);
	pos += data_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_import_ext_ecc_keypair( int app_id, int container_id, int key_spec, unsigned char * ecc_key_blob, int key_blob_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0x2C, key_spec, 0x00, "ImportExtECCKeyPair");

	unsigned char data[4096] = {0};

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;


	memcpy(data + pos, ecc_key_blob, key_blob_len);
	pos += key_blob_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_gen_ext_ecc_keypair()
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0xC0, 0X80, 2, 1, "GenExtSM2KeyPair");
	newApdu->set_comm_timeout(MAX_ECC_TIMOUT);

	unsigned char data[32] = {0};
	int bits = 256; 

	mk_utility::fill_buff_with_word_value_be(bits, data);

	newApdu->set_lc_data(data, 2);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_export_ext_ecc_pub_key()
{
	apdu * newApdu = new apdu(SC_APDU_CASE_2_EXT, 0xC0, 0X82, 2, 0, "ExportExtSM2PubKey");

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_import_ext_ecc_keypair_to_ram(unsigned char * keypair_blob, int blob_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0X84, 2, 0, "ImportExtECCKeyPairToRam");
	
	newApdu->set_lc_data(keypair_blob, blob_len);

	return newApdu;
}

apdu * apdu_ecc_manager::create_apdu_ram_ecc_operation( int mode, unsigned char * input, int input_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0xC0, 0X88, mode, 0, "ECCOperation");

	unsigned char data[4096] = {0};

	int pos = 0;
	if(mode == 1)//decrypt
	{
		mk_utility::fill_buff_with_dword_value_be(input_len, data);
		pos += 4;
	}

	memcpy(data + pos, input, input_len);
	pos += input_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}
